package com.lds.erp.service;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lds.erp.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import com.dph.common.utils.core.exception.BizError;
import com.dph.common.utils.enums.OperationStatus;


/**
 * @author hongsir
 * @apiNote 全局异常处理器
 * 处理优先级
 * - @RestControllerAdvice @ExceptionHandler
 * - ⬇️
 * - extends ResponseEntityExceptionHandler @Override handleExceptionInternal
 * - ⬇️
 * - implements ErrorController >> @Override  getErrorPath() >> @RestController @RequestMapping(value = "/error")
 * @date 2018/9/18 下午2:11
 */
@RestController
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler
        implements ErrorController {
    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * C1 手动挡,用于捕捉 throw new BizError();的异常
     */
    @ExceptionHandler(BizError.class)
    public Object customExceptionHandler(HttpServletRequest request, HttpServletResponse response, final Exception e) {
        response.setStatus(HttpStatus.OK.value());
        BizError exception = (BizError) e;
        log.error("请求接口异常C1:{}，<BizError>:{}", request.getRequestURI(), exception.getMessage());
        return JsonUtils.fail(exception.getMessage());
    }

    /**
     * C2 自动挡
     */
    @ExceptionHandler(RuntimeException.class)
    public Object runtimeExceptionHandler(HttpServletRequest request, HttpServletResponse response, final Exception e) {
        response.setStatus(HttpStatus.OK.value());
        // 解决因dubbo转换自定义异常导致异常class不对的问题
        if (e.getMessage().contains("BizError")) {
            String msg = e.getMessage();
            return JsonUtils.fail(msg.substring(msg.indexOf(":") + 2, msg.indexOf("\n")));
        }
        RuntimeException exception = (RuntimeException) e;
        log.error("请求接口异常C2:{}，<RuntimeException>:", request.getRequestURI(), exception);
        return JsonUtils.fail(OperationStatus.EXCEPTION.message());
    }


    /**
     * B1 老司机
     * 当C1 C2 均无法驾驶时,请使用老司机套路
     * 在controller里面内容执行之前，校验一些参数不匹配啊，Get post方法不对啊,404啊之类的
     * 注意,部分异常,如404(会默认找视图);需要在属性文件中配置以下属性才生效
     * #出现错误时, 直接抛出异常
     * spring.mvc.throw-exception-if-no-handler-found=true
     * #不要为我们工程中的资源文件建立映射
     * spring.resources.add-mappings=false
     *
     * @param ex      异常
     * @param body    response
     * @param headers 返回的headers
     * @param status  http状态
     * @param request 当前请求
     * @return JSON字符串
     */
    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception ex,
                                                             Object body,
                                                             HttpHeaders headers,
                                                             HttpStatus status,
                                                             WebRequest request) {
        StringBuilder message = new StringBuilder();
        Object data = null;
        //404
        if (ex instanceof NoHandlerFoundException) {
            NoHandlerFoundException exception = (NoHandlerFoundException) ex;
            message.append("您访问的资源不存在");
            data = exception.getRequestURL();
            log.error("请求异常B1:{}，<NoHandlerFoundException-404>:{}", data, message.toString());
        }
        //参数类型异常
        else if (ex instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException exception = (MethodArgumentTypeMismatchException) ex;
            String paramName = exception.getName();
            String typeName = exception.getRequiredType().getSimpleName();
            Object paramValue = exception.getValue();
            message.append("参数[").append(paramName).append("]类型错误,值[").append(paramValue).append("]不是").append(typeName).append("类型");
            log.error("请求异常B1:{}，<MethodArgumentTypeMismatchException>:{}", request, message.toString());
        }
        // 校验异常
        else if (ex instanceof BindException) {
            BindException exception = (BindException) ex;
            List<ObjectError> errors = exception.getAllErrors();
            for (ObjectError error : errors) {
                message.append(error.getDefaultMessage()).append(";");
            }
        }
        // 校验异常
        else if (ex instanceof HttpMessageNotReadableException) {
            message.append("请求数据丢失,ServletWebRequest输入流被拦截了");
        }
        // 校验异常
        else if (ex instanceof MissingServletRequestParameterException) {
            message.append("缺少必传参数:").append(ex.getMessage());
        }
        // 校验异常
        else if (ex instanceof HttpMediaTypeNotSupportedException) {
            message.append("请求头错误,post必须为:[").append("application/json;charset=UTF-8]");
        }
        // 纵向扩展的特殊异常...
        else {
            log.error("请求异常B1:{}", request, ex);
        }
        return new ResponseEntity<>(JsonUtils.fail(message.toString(), data), HttpStatus.OK);

    }

    /**
     * A1 考试
     *
     * @return 重置springboot的错误路径
     */
    @Override
    public String getErrorPath() {
        return "/error";
    }

    /**
     * A1 路考
     *
     * @param request 当前请求
     * @return 当老司机都无奈的时候, 统一返回系统繁忙
     */
    @RequestMapping(value = "/error")
    public Object error(HttpServletRequest request, Exception e) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        Object str = request.getAttribute("javax.servlet.error.request_uri");
        log.error("请求接口异常状态:{},url:{}", statusCode,str);
        // 最终方式直接抛出系统繁忙!!!!因为本大爷已经不知道哪儿出了异常拉,404占99%的几率
        return JsonUtils.fail("系统繁忙,请稍后再试!");
    }


}
